package org.example.util;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.List;
import java.util.ArrayList;

/**
 * 文件切片下载工具类
 */
public class FileDownloadUtil {
    
    private static final int THREAD_COUNT = 4; // 下载线程数
    private static final int CHUNK_SIZE = 1024 * 1024; // 每个切片1MB
    private static final String DOWNLOAD_DIR = "downloads"; // 下载目录
    
    /**
     * 多线程切片下载文件
     */
    public static String downloadFile(String fileUrl, String serverBaseUrl) throws Exception {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("HEAD");
        
        // 获取文件大小
        long fileSize = connection.getContentLengthLong();
        if (fileSize <= 0) {
            throw new Exception("无法获取文件大小");
        }
        
        // 生成文件名
        String fileName = generateFileName(fileUrl);
        File downloadDir = new File(DOWNLOAD_DIR);
        if (!downloadDir.exists()) {
            downloadDir.mkdirs();
        }
        
        File targetFile = new File(downloadDir, fileName);
        
        // 如果文件已存在，返回现有文件的访问地址
        if (targetFile.exists() && targetFile.length() == fileSize) {
            return serverBaseUrl + "/downloads/" + fileName;
        }
        
        // 创建临时文件
        RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw");
        randomAccessFile.setLength(fileSize);
        randomAccessFile.close();
        
        // 计算每个线程下载的字节范围
        long chunkSize = fileSize / THREAD_COUNT;
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            long startByte = i * chunkSize;
            long endByte = (i == THREAD_COUNT - 1) ? fileSize - 1 : (i + 1) * chunkSize - 1;
            
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                try {
                    downloadChunk(fileUrl, targetFile, startByte, endByte);
                } catch (Exception e) {
                    throw new RuntimeException("下载切片失败", e);
                }
            }, executor);
            
            futures.add(future);
        }
        
        // 等待所有线程完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        executor.shutdown();
        
        return serverBaseUrl + "/downloads/" + fileName;
    }
    
    /**
     * 下载文件切片
     */
    private static void downloadChunk(String fileUrl, File targetFile, long startByte, long endByte) throws Exception {
        URL url = new URL(fileUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestProperty("Range", "bytes=" + startByte + "-" + endByte);
        
        try (InputStream inputStream = connection.getInputStream();
             RandomAccessFile randomAccessFile = new RandomAccessFile(targetFile, "rw")) {
            
            randomAccessFile.seek(startByte);
            byte[] buffer = new byte[8192];
            int bytesRead;
            
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                randomAccessFile.write(buffer, 0, bytesRead);
            }
        }
    }
    
    /**
     * 从URL生成文件名
     */
    private static String generateFileName(String fileUrl) {
        String fileName = fileUrl.substring(fileUrl.lastIndexOf('/') + 1);
        if (fileName.isEmpty() || !fileName.contains(".")) {
            fileName = "download_" + System.currentTimeMillis() + ".bin";
        }
        // 移除URL参数
        int paramIndex = fileName.indexOf('?');
        if (paramIndex > 0) {
            fileName = fileName.substring(0, paramIndex);
        }
        return fileName;
    }
    
    /**
     * 获取下载目录路径
     */
    public static String getDownloadDir() {
        return DOWNLOAD_DIR;
    }
}
